package msgbackend

import (
	"runtime"
	"errors"
	"encoding/json"
	"fmt"
	"io"
  "strings"
	"GoServer/database"
	"GoServer/msg"
	"GoServer/user"
	"sync"
	"time"

	"github.com/garyburd/redigo/redis"
	"github.com/name5566/leaf/log"
)

type Receivedata struct {
	Username string      `json:"UserName"`
	Msgdata  interface{} `json:"MsgData"`
	MsgType  int         `json:"MsgType"`
	Sendtime int         `json:"SendTime"`
	Sender   string      `json:"Sender"`
}

type MessageQuer struct {
		MessageName string
		Data []MessageQuer
		Msgdata []interface{}
}



type SendtoClient interface {
	SendClicent()
}

// var rsContent redis.Conn
var SendToService string
var icount int

var g_countMangerChan chan []interface{}
var lock sync.Mutex
var (
	rsContent *redis.Pool
	rsGetContent *redis.Pool
)

//初始化连接发送消息
func RedisContent() bool {
	var (
		host string
		auth string
		db   int
	)
	g_countMangerChan = make(chan []interface{})
	startCountTimer()
	g_JsonConfig := database.G_JsonConfig
	host = g_JsonConfig.RedisHost
	auth = g_JsonConfig.RedisKey
	db = 0
	rsContent = &redis.Pool{
			MaxIdle:     100,
			MaxActive:   10000,
			IdleTimeout: 180 * time.Second,
			Dial: func() (redis.Conn, error) {
					c, err := redis.Dial("tcp", host, redis.DialPassword(auth), redis.DialDatabase(db))
					if nil != err {
							return nil, err
					}
					return c, nil
			},
			TestOnBorrow: func(c redis.Conn, t time.Time) error {
					if time.Since(t) < time.Minute {
							return nil
					}
					_, err := c.Do("PING")
					return err
			},
	}
	rsGetContent = &redis.Pool{
		MaxIdle:     100,
		MaxActive:   10000,
		IdleTimeout: 180 * time.Second,
		Dial: func() (redis.Conn, error) {
				c, err := redis.Dial("tcp", host, redis.DialPassword(auth), redis.DialDatabase(db))
				if nil != err {
						return nil, err
				}
				return c, nil
		},
		TestOnBorrow: func(c redis.Conn, t time.Time) error {
				if time.Since(t) < time.Minute {
						return nil
				}
				_, err := c.Do("PING")
				return err
		},
	}
  return true
}


//

//轮询读取lpop
func MsgRouterGetprop() {
	g_JsonConfig := database.G_JsonConfig
	GategetserverTopic := g_JsonConfig.PrivatetopicAll.Gategetserver
	for {
		//// TODO: ----- 需要修改为  RPOP
		// r, getErr := redis.Values(rs.Do("BRPOP", GategetserverTopic, 3))
		r, getErr := redis.Values(redoread("BRPOP", GategetserverTopic,3))
		if getErr != nil {
			//log.Debug("BRPOP err: %v", getErr.Error())
		} else {
		//重新连接监听
		g_countMangerChan<-r
		runtime.Gosched()
		return
		}
	}

}

func cleardata(data []interface{}) {
  //启动等待组
	for _, v := range data {
		icount++
		stringres := fmt.Sprintf("%s", v.([]byte))
		log.Debug("cleardata : %v | %v",stringres,icount)
		MsgRouterReader(stringres)
	}
}

// 启动通道监听
func startCountTimer() {
	go func() {
		for {
			select {
			case data := <-g_countMangerChan:
				cleardata(data)
				break;
		}
		}
	}()
}


func ChargeServiceInit() bool {
	go func() {
		for {
			//消息监听
			MsgRouterGetprop()
			log.Debug("redis reconnecting")
		}
	}()
	return true
}

//redis自检
func redisping(c redis.Conn) {
	_, err := c.Do("ping")
    if err != nil {
		//重新连接操作
	    RedisContent()
        log.Debug("[ERROR] ping redis fail", err)
	}

}

//扫描redis连接
func IsConnError(err error) bool {
	var needNewConn bool

	if err == nil {
			return false
	}

	if err == io.EOF {
			needNewConn = true
	}
	if strings.Contains(err.Error(), "use of closed network connection") {
			needNewConn = true
	}
	if strings.Contains(err.Error(), "connect: connection refused") {
			needNewConn = true
	}
	return needNewConn
}


//消息中转写入
func MsgRouterWriter(data string) {
	g_JsonConfig := database.G_JsonConfig
	ToserverTopic := g_JsonConfig.PrivatetopicAll.Gateserver

	// TODO:这里需要做映射，通过userName检索对应的topic
	// lock.Lock()
	_, err := redo("LPUSH", ToserverTopic, data)
	//lock.Unlock()
	// _, err := rsContent.Do("LPUSH", ToserverTopic, data)
	if err != nil {
		log.Debug("LPUSH err: %v", err.Error())
	}
}

//消息中转读取
func MsgRouterReader(data string) {
	var receive Receivedata
	str := data
	err := json.Unmarshal([]byte(str), &receive)
	//定义写入哪个频道
	Send := receive.Sender

	// g_JsonConfig := database.G_JsonConfig
	//g_JsonConfig.PrivatetopicAll.Gateserver = Send
	if Send != "" {
		database.G_JsonConfig.PrivatetopicAll.Gateserver = Send
	}
	//	str := string(data)
	if err == nil {
		SendDataToClient(receive)
		// log.Debug(g_JsonConfig.PrivatetopicAll.Gateserver)
	} else {
		log.Debug(err.Error())
	}

}

func SendDataToClient(r SendtoClient) {
	r.SendClicent()
}

func (r Receivedata) SendClicent() {
	// print(r.Msgdata)
	mjson, _ := json.Marshal(r.Msgdata)
	mString := string(mjson)
	// log.Debug("proto c++")
	// log.Debug(mString)
	res := user.UserInfoCacheGet(r.Username)
	if res != nil {
		//fmt.Println(res)
		//加锁
		resposeUser := res.Angets

		resposeUser.WriteMsg(&msg.Respose{
			Result: mString,
		})

	}
}

//func
func Setloginchannel(data string) {
	g_JsonConfig := database.G_JsonConfig
	ToserverTopic := g_JsonConfig.PrivatetopicAll.Loginchannel
	// _, err := rsContent.Do("LPUSH", ToserverTopic, data)
	lock.Lock()
	_, err := redo("LPUSH", ToserverTopic, data)
	lock.Unlock()
	if err != nil {
		log.Debug("LPUSH Loginchanenl err: %v", err.Error())
	}
}

// 在pool加入TestOnBorrow方法来去除扫描坏连接
func redo(command string, opt ...interface{}) (interface{}, error) {
	rd := rsContent.Get()
	defer rd.Close()

	var conn redis.Conn
	var err error
	var maxretry = 3
	var needNewConn bool

	resp, err := rd.Do(command, opt...)
	needNewConn = IsConnError(err)
	if needNewConn == false {
			return resp, err
	} else {
			conn, err = rsContent.Dial()
	}

	for index := 0; index < maxretry; index++ {
			if conn == nil && index+1 > maxretry {
					return resp, err
			}
			if conn == nil {
					conn, err = rsContent.Dial()
			}
			if err != nil {
					continue
			}

			resp, err := conn.Do(command, opt...)
			needNewConn = IsConnError(err)
			if needNewConn == false {
					return resp, err
			} else {
					conn, err = rsContent.Dial()
			}
	}

	conn.Close()
	return "", errors.New("redis error")
}

func redoread(command string, opt ...interface{}) (interface{}, error) {
	rd := rsGetContent.Get()
	defer rd.Close()

	var conn redis.Conn
	var err error
	var maxretry = 3
	var needNewConn bool

	resp, err := rd.Do(command, opt...)
	needNewConn = IsConnError(err)
	if needNewConn == false {
			return resp, err
	} else {
			conn, err = rsContent.Dial()
	}

	for index := 0; index < maxretry; index++ {
			if conn == nil && index+1 > maxretry {
					return resp, err
			}
			if conn == nil {
					conn, err = rsContent.Dial()
			}
			if err != nil {
					continue
			}

			resp, err := conn.Do(command, opt...)
			needNewConn = IsConnError(err)
			if needNewConn == false {
					return resp, err
			} else {
					conn, err = rsContent.Dial()
			}
	}

	conn.Close()
	return "", errors.New("redis error")
}
